# 导入相关库
import numpy as np  # 导入numpy库，用于处理数组和数值计算
import math
from jili.tool.convert import data_dropna
import matplotlib.pyplot as plt  # 导入matplotlib的绘图模块，用于可视化
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #该语句解决图像中的“-”负号的乱码问题
def chart_boxplot(box1,url=None,sample=[600,60],stdn=True,flags_info={},boxn=[600,60]):
    box1 = data_dropna(box1)
    m0=np.mean(box1)
    std0=np.std(box1)
    new0=box1.to_list()[-1]
    bins=int(len(box1)/10)
    if bins>100:
        bins=100
    plt.figure(figsize=(16, 9), dpi=100)
    plt.subplot(2, 2, 1)
    # axes[0].figure(figsize=(9, 3.5), dpi=200)
    plt.plot(box1)
    # plt.suptitle()
    plt.title('时序图')
    ax1=plt.subplot(2, 2, 2)
    if sample:
        box1.hist(bins=bins, label="全部",alpha=0.7)
        for i in sample:
            box2 = box1[-i:]
            box2.hist(bins=bins, label="最近" + str(i),alpha=0.7)
        plt.legend()
    else:
        box1.hist(bins=bins,alpha=0.7)
    plt.title('频率分布')
    ax2=plt.subplot(2, 2, 3)
    if sample:
        hist2, bin_edges2 = np.histogram(box1, bins=bins, density=True)
        ax2.hist(bin_edges2[:-1], bin_edges2, weights=hist2,alpha=0.7,label="全部")
        max0=np.max(bin_edges2)
        mu = box1.mean()
        sigma = box1.std()
        x = np.linspace(box1.min(), box1.max(), 100)
        ndf = np.exp(-(x - mu) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
        new00=np.exp(-(new0 - mu) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
        ax2.plot(x, ndf, 'r-',label="全部密度")
        for i in sample:
            box2=box1[-i:]
            hist2, bin_edges2 = np.histogram(box2, bins=int(i/9), density=True)
            ax2.hist(bin_edges2[:-1], bin_edges2, weights=hist2, alpha=0.7, label="最近"+str(i))
            mu = box2.mean()
            sigma = box2.std()
            x = np.linspace(box2.min(), box2.max(), 100)
            ndf = np.exp(-(x - mu) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
            ax2.plot(x, ndf, '--',label="最近"+str(i)+"密度")
        plt.legend()
    else:
        hist2, bin_edges2 = np.histogram(box1, bins=bins, density=True)
        max0 = np.max(bin_edges2)
        ax2.hist(bin_edges2[:-1], bin_edges2, weights=hist2,alpha=0.7)
        mu = box1.mean()
        sigma = box1.std()
        x = np.linspace(box1.min(), box1.max(), 100)
        ndf = np.exp(-(x - mu) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
        new00 = np.exp(-(new0 - mu) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
        ax2.plot(x, ndf, 'r-',label="概率密度")
    smin = math.floor((box1.min() - m0) / std0)
    smax = math.ceil((box1.max() - m0) / std0)
    plt.vlines(new0, ymin=max0/4, ymax=max0/3, ls='-', lw=2, color="b", label='最新值', alpha=.5, )
    for k,v in flags_info.items():
        plt.vlines(v, ymin=max0/4, ymax=max0/3, ls='-', lw=3, color="r", label=k, alpha=.5, )
    for ni in range(smin, smax + 1):
        if ni != 0:
            v0 = m0 + ni * std0
            plt.vlines(v0, ymin=max0/4, ymax=max0/3, ls='--', lw=2,alpha=0.5, label=str(ni) + 'σ')
        else:
            plt.vlines(m0, ymin=max0/4, ymax=max0/3, ls='-', lw=2,alpha=0.5, color="k", label='平均值')
    plt.title('标准正态概率密度')
    plt.legend()
    # 计算箱图的第一四分位数（Q1）和第三四分位数（Q3）
    q1 = np.quantile(box1, .25)
    q3 = np.quantile(box1, .75)
    # 计算四分位距（IQR）
    iqr = q3 - q1
    # 计算箱图的上下边界（通常用于识别异常值）
    lower_boundary = q1 - 1.5 * iqr
    upper_boundary = q3 + 1.5 * iqr
    # 对随机数数组进行排序
    sort = np.sort(box1)
    # 定义用于标记不同统计量的颜色
    colors = ['#6b2983', '#5f82cb', '#00d6ff',
              '#e77ca3', '#93003a']
    ax3=plt.subplot(2, 2, 4)
    plt.title('离散分布')
    # 设置绘图的大小和分辨率
    # plt.figure(figsize=(9, 3.5), dpi=200)
    # 绘制箱图，这里设置箱图为水平方向
    if boxn:
        box2=[box1]
        lables=["全部"]
        for i in boxn:
            lables.append("最近"+str(i))
            box2.append(box1[-i:])
        plt.boxplot(box2, vert=False,labels=lables)
    else:
        plt.boxplot(box1, vert=False)
    # 使用散点图标记每个随机数的位置，颜色为红色，透明度为0.4
    # plt.scatter(box1, np.linspace(1.3, 1.3, box1.shape[0]), color='r', alpha=.4, label='密度')
    # 使用垂直线标记箱图的上下边界、最小值、次小值（除去最小值）、最大值和异常值

    if stdn:
        #内外σ
        smin=math.floor((box1.min()-m0)/std0)
        smax=math.ceil((box1.max()-m0)/std0)
        for ni in range(smin,smax+1):
            if ni!=0:
                v0=m0+ni*std0
                plt.vlines(v0, ymin=0.2, ymax=.65, ls=':', lw=1.25, label=str(ni)+'σ')
            else:
                plt.vlines(m0, ymin=0.2, ymax=.65, ls='-', lw=1.25, color="k", label='平均值', alpha=.5, )
    else:
        plt.vlines(lower_boundary, ymin=0.2, ymax=.65, alpha=1, ls='-.', color='k', label='Q1-1.5IQR')
        plt.vlines(sort[0], ymin=0.2, ymax=.65, ls='-', lw=1.25, color=colors[2], label='最小值')
        plt.vlines(sort[-2], ymin=0.2, ymax=.65, ls='-', lw=1.25, color=colors[3], label='最大值')
        plt.vlines(sort[-1], ymin=0.2, ymax=.65, ls='-', lw=1.25, color=colors[4], label='异常值',alpha=.5,)
        plt.vlines(upper_boundary, ymin=0.2, ymax=.65, alpha=.5, ls='--', color='k', label='Q3+1.5IQR')
        plt.vlines(np.median(box1), ymin=0.2, ymax=.65, ls='-', lw=1.25, color="b", label='中位数',alpha=.5,)
        plt.vlines(m0, ymin=0.2, ymax=.65, ls='-', lw=1.25, color="k", label='平均值',alpha=.5,)
    plt.vlines(new0, ymin=0.2, ymax=.65, ls='-', lw=1.25, color="b", label='最新值', alpha=.5, )
    for k,v in flags_info.items():
        plt.vlines(v, ymin=0.2, ymax=.65, ls='-', lw=2, color="r", label=k, alpha=.5, )

    plt.annotate(np.round(lower_boundary, 2), xy=(lower_boundary, 0.7), ha='center', color='k')
    plt.annotate(np.round(sort[0], 2), xy=(sort[0], 0.7), ha='center', color=colors[2])
    plt.annotate(np.round(sort[-2], 2), xy=(sort[-2], 0.7), ha='center', color=colors[3])
    plt.annotate(np.round(sort[-1], 2), xy=(sort[-1], 0.7), ha='center', color=colors[4])
    plt.annotate(np.round(upper_boundary, 2), xy=(upper_boundary, 0.8), ha='center', color='k')
    # 设置x轴的范围相同
    ax1.set_xlim(ax3.get_xlim())
    # ax1.set_xticks(ax2.get_xticks())
    # 显示图例，并将其放置在图的右上角
    plt.legend()
    if url is None:
        # 显示图表
        plt.show()
    else:
        plt.savefig(url, bbox_inches='tight', pad_inches=0.01, dpi=100)
        plt.close()
    return url